"""
bilibili_api.manga

漫画相关操作
"""

import base64
import datetime
from enum import Enum
from urllib.parse import urlparse
from typing import Dict, List, Union, Optional

from bilibili_api.utils.utils import get_api
from bilibili_api.exceptions import ArgsException
from bilibili_api.utils.picture import Picture
from bilibili_api.utils.network import HEADERS, Api, Credential

from Cryptodome.PublicKey import ECC

API = get_api("manga")


class MangaOrderType(Enum):
    """
    漫画排序方式

    - FOLLOW: 追漫顺序
    - UPDATE: 更新时间
    - READING: 最近阅读
    - FREE: 等免
    """
    FOLLOW = {"order": 1, "wait_free": 0}
    UPDATE = {"order": 2, "wait_free": 0}
    READING = {"order": 3, "wait_free": 0}
    FREE = {"order": 3, "wait_free": 1}


class MangaIndexFilter:
    """
    漫画索引筛选器类。
    """

    class Area(Enum):
        """
        漫画索引筛选器的地区枚举类。

        - ALL: 全部
        - CHINA: 大陆
        - JAPAN: 日本
        - SOUTHKOREA: 韩国
        - OTHER: 其他
        """

        ALL = -1
        CHINA = 1
        JAPAN = 2
        SOUTHKOREA = 6
        OTHER = 5

    class Order(Enum):
        """
        漫画索引筛选器的排序枚举类。

        - HOT: 人气推荐
        - UPDATE: 更新时间
        - RELEASE_DATE: 上架时间
        """

        HOT = 0
        UPDATE = 1
        RELEASE_DATE = 3

    class Status(Enum):
        """
        漫画索引筛选器的状态枚举类。

        - ALL: 全部
        - FINISHED: 完结
        - UNFINISHED: 连载
        """

        ALL = -1
        FINISHED = 1
        UNFINISHED = 0

    class Payment(Enum):
        """
        漫画索引筛选器的付费枚举类。

        - ALL: 全部
        - FREE: 免费
        - PAID: 付费
        - WILL_BE_FREE: 等就免费
        """

        ALL = -1
        FREE = 1
        PAID = 2
        WILL_BE_FREE = 3

    class Style(Enum):
        """
        漫画索引筛选器的风格枚举类。

        - ALL: 全部
        - WARM: 热血
        - ANCIENT: 古风
        - FANTASY: 玄幻
        - IMAGING: 奇幻
        - SUSPENSE: 悬疑
        - CITY: 都市
        - HISTORY: 历史
        - WUXIA: 武侠仙侠
        - GAME: 游戏竞技
        - PARANORMAL: 悬疑灵异
        - ALTERNATE: 架空
        - YOUTH: 青春
        - WEST_MAGIC: 西幻
        - MORDEN: 现代
        - POSITIVE: 正能量
        - SCIENCE_FICTION: 科幻
        """

        ALL = -1
        WARM = 999
        ANCIENT = 997
        FANTASY = 1016
        IMAGING = 998
        SUSPENSE = 1023
        CITY = 1002
        HISTORY = 1096
        WUXIA = 1092
        GAME = 1088
        PARANORMAL = 1081
        ALTERNATE = 1063
        YOUTH = 1060
        WEST_MAGIC = 1054
        MORDEN = 1048
        POSITIVE = 1028
        SCIENCE_FICTION = 1027


class Manga:
    """
    漫画类

    Attributes:
        credential (Credential): 凭据类。
    """

    def __init__(self, manga_id: int, credential: Optional[Credential] = None):
        """
        Args:
            manga_id   (int)              : 漫画 id

            credential (Credential | None): 凭据类. Defaults to None.
        """
        credential = credential if credential else Credential()
        self.__manga_id = manga_id
        self.credential: Credential = credential
        self.__info: Optional[Dict] = None

    def get_manga_id(self) -> int:
        """
        获取漫画的 id

        Returns:
            int: 漫画的 id
        """
        return self.__manga_id

    # async def get_info(self) -> dict:
    #     """
    #     # 此函数已失效 2025-01-04
    #     获取漫画信息

    #     Returns:
    #         dict: 调用 API 返回的结果
    #     """
    #     api = API["info"]["detail"]
    #     params = {"device": "pc", "platform": "web", "nov": 25}
    #     data = {"comic_id": self.get_manga_id()}
    #     return (
    #         await Api(**api, credential=self.credential, no_csrf=True, json_body=True)
    #         .update_params(**params)
    #         .update_data(**data)
    #         .result
    #     )

    # async def __get_info_cached(self) -> dict:
    #     """
    #     # 此函数已失效 2025-01-04
    #     获取漫画信息，如果有缓存则使用缓存。
    #     """
    #     if self.__info == None:
    #         self.__info = await self.get_info()
    #     return self.__info

    # async def get_episode_info(
    #     self,
    #     episode_count: Optional[Union[int, float]] = None,
    #     episode_id: Optional[int] = None,
    # ) -> dict:
    #     """
    #     # 此函数已失效 2025-01-04
    #     获取某一话信息

    #     Args:
    #         episode_count (int | float | None): 第几话.

    #         episode_id    (int | None)        : 对应的话的 id. 可以通过 `get_episode_id` 获取。

    #     Returns:
    #         dict: 对应的话信息

    #     **注意：episode_count 和 episode_id 中必须提供一个参数。**
    #     """
    #     info = await self.__get_info_cached()
    #     for ep in info["ep_list"]:
    #         if episode_count == None:
    #             if ep["id"] == episode_id:
    #                 return ep
    #         elif episode_id == None:
    #             if ep["ord"] == episode_count:
    #                 return ep
    #         else:
    #             raise ArgsException("episode_count 和 episode_id 中必须提供一个参数。")
    #     raise ArgsException("未找到对应的话")

    # async def get_episode_id(
    #     self, episode_count: Optional[Union[int, float]] = None
    # ) -> int:
    #     """
    #     # 此函数已失效 2025-01-04
    #     获取某一话的 id

    #     Args:
    #         episode_count (int | float | None): 第几话.

    #     Returns:
    #         int: 对应的话的 id
    #     """
    #     return (await self.get_episode_info(episode_count=episode_count))["id"]

    # async def get_images_url(
    #     self,
    #     episode_count: Optional[Union[int, float]] = None,
    #     episode_id: Optional[int] = None,
    # ) -> dict:
    #     """
    #     # 此函数已失效 2025-01-04
    #     获取某一话的图片链接。(未经过处理，所有的链接无法直接访问)

    #     获取的图片 url 请传入 `manga.manga_image_url_turn_to_Picture` 函数以转换为 `Picture` 类。

    #     Args:
    #         episode_count (int | float | None): 第几话.

    #         episode_id    (int | None)        : 对应的话的 id. 可以通过 `get_episode_id` 获取。

    #     Returns:
    #         dict: 调用 API 返回的结果

    #     **注意：episode_count 和 episode_id 中必须提供一个参数。**
    #     """
    #     if episode_id == None:
    #         if episode_count == None:
    #             raise ArgsException("episode_count 和 episode_id 中必须提供一个参数。")
    #         episode_id = await self.get_episode_id(episode_count)
    #     api = API["info"]["episode_images"]
    #     params = {"device": "pc", "platform": "web", "nov": 25}
    #     data = {"ep_id": episode_id}
    #     return (
    #         await Api(**api, credential=self.credential, no_csrf=True)
    #         .update_params(**params)
    #         .update_data(**data)
    #         .result
    #     )

    # async def get_images(
    #     self,
    #     episode_count: Optional[Union[int, float]] = None,
    #     episode_id: Optional[int] = None,
    # ) -> List[Dict]:
    #     """
    #     # 此函数已失效 2025-01-04
    #     获取某一话的所有图片

    #     Args:
    #         episode_count (int | float | None): 第几话.

    #         episode_id    (int | None)        : 对应的话的 id. 可以通过 `get_episode_id` 获取。

    #     Returns:
    #         List[Picture]: 所有的图片

    #     **注意：episode_count 和 episode_id 中必须提供一个参数。**

    #     注意事项：此函数速度非常慢并且失败率高
    #     """
    #     data = await self.get_images_url(
    #         episode_count=episode_count, episode_id=episode_id
    #     )
    #     pictures: List[Dict] = []

    #     async def get_real_image_url(url: str) -> str:
    #         token_api = API["info"]["image_token"]
    #         params = {"device": "pc", "platform": "web", "nov": 25}
    #         datas = {"urls": f'["{url}"]'}
    #         token_data = (
    #             await Api(
    #                 **token_api,
    #                 credential=self.credential,
    #                 no_csrf=True,
    #             )
    #             .update_params(**params)
    #             .update_data(**datas)
    #             .result
    #         )
    #         return token_data[0]["url"] + "?token=" + token_data[0]["token"]

    #     for img in data["images"]:
    #         url = await get_real_image_url(img["path"])
    #         pictures.append(
    #             {
    #                 "x": img["x"],
    #                 "y": img["y"],
    #                 "picture": Picture.from_content(
    #                     (await httpx.AsyncClient().get(url, headers=HEADERS)).content,
    #                     "jpg",
    #                 ),
    #             }
    #         )
    #     return pictures


# async def manga_image_url_turn_to_Picture(
#     url: str, credential: Optional[Credential] = None
# ) -> Picture:
#     """
#     # 此函数已失效 2025-01-04
#     将 Manga.get_images_url 函数获得的图片 url 转换为 Picture 类。

#     Args:
#         url        (str)               : 未经处理的漫画图片链接。

#         credential (Credential | None): 凭据类. Defaults to None.

#     Returns:
#         Picture: 图片类。
#     """
#     url = urlparse(url).path
#     credential = credential if credential else Credential()

#     def get_m1():
#         key = ECC.generate(curve="P-256")
#         pubKey = key.public_key().export_key(format="raw")
#         return base64.b64encode(pubKey).decode("ascii")

#     async def get_real_image_url(url: str) -> str:
#         token_api = API["info"]["image_token"]
#         params = {"device": "pc", "platform": "web", "nov": 25}
#         datas = {"urls": f'["{url}@1100w.avif"]', "m1": get_m1()}
#         token_data = (
#             await Api(**token_api, credential=credential, no_csrf=True, json_body=True)
#             .update_params(**params)
#             .update_data(**datas)
#             .result
#         )
#         return token_data[0]["complete_url"]

#     url = await get_real_image_url(url)
#     return await Picture.async_load_url(url)


async def set_follow_manga(
    manga: Manga, status: bool = True, credential: Optional[Credential] = None
) -> dict:
    """
    设置追漫

    Args:
        manga      (Manga)     : 漫画类。

        status     (bool)      : 设置是否追漫。是为 True，否为 False。Defaults to True.

        credential (Credential): 凭据类。
    """
    if credential == None:
        if manga.credential.has_sessdata() and manga.credential.has_bili_jct():
            credential = manga.credential
        else:
            credential = Credential()
    credential.raise_for_no_sessdata()
    credential.raise_for_no_bili_jct()
    if status == True:
        api = API["operate"]["add_favorite"]
    else:
        api = API["operate"]["del_favorite"]

    params = {"device": "pc", "platform": "web", "nov": 25}
    data = {"comic_ids": str(manga.get_manga_id())}
    return (
        await Api(**api, credential=credential)
        .update_params(**params)
        .update_data(**data)
        .result
    )


async def get_followed_manga(
    pn: int = 1, ps: int = 15, order: MangaOrderType = MangaOrderType.FOLLOW, credential: Optional[Credential] = None
) -> List[Manga]:
    """
    获取追漫列表

    Args:
        pn         (int)           : 页码。Defaults to 1.
        ps         (int)           : 每页数量。Defaults to 18.
        order      (MangaOrderType): 排序方式。Defaults to MangaOrderType.FOLLOW.
        credential (Credential)    : 凭据类.

    Returns:
        List[Manga]: 追漫列表
    """
    credential = credential if credential else Credential()
    credential.raise_for_no_sessdata()
    api = API["info"]["followed_manga"]
    params = {"device": "pc", "platform": "web", "nov": 25}
    data = {"page_num": pn, "page_size": ps}
    data.update(order.value)
    return (
        await Api(**api, credential=credential, no_csrf=True)
        .update_data(**data)
        .update_params(**params)
        .result
    )


# async def get_raw_manga_index(
#     area: MangaIndexFilter.Area = MangaIndexFilter.Area.ALL,
#     order: MangaIndexFilter.Order = MangaIndexFilter.Order.HOT,
#     status: MangaIndexFilter.Status = MangaIndexFilter.Status.ALL,
#     payment: MangaIndexFilter.Payment = MangaIndexFilter.Payment.ALL,
#     style: MangaIndexFilter.Style = MangaIndexFilter.Style.ALL,
#     pn: int = 1,
#     ps: int = 18,
#     credential: Credential = None,
# ) -> list:
#     """
#     # 失效 2025-01-27
#     获取漫画索引

#     Args:
#         area    (MangaIndexFilter.Area)   : 地区。Defaults to MangaIndexFilter.Area.ALL.

#         order   (MangaIndexFilter.Order)  : 排序。Defaults to MangaIndexFilter.Order.HOT.

#         status  (MangaIndexFilter.Status) : 状态。Defaults to MangaIndexFilter.Status.ALL.

#         payment (MangaIndexFilter.Payment): 支付。Defaults to MangaIndexFilter.Payment.ALL.

#         style   (MangaIndexFilter.Style)  : 风格。Defaults to MangaIndexFilter.Style.ALL.

#         pn      (int)                     : 页码。Defaults to 1.

#         ps      (int)                     : 每页数量。Defaults to 18.

#         credential (Credential)           : 凭据类. Defaults to None.

#     Returns:
#         list: 调用 API 返回的结果
#     """
#     credential = credential if credential else Credential()
#     api = API["info"]["index"]
#     params = {"device": "pc", "platform": "web", "nov": 25}
#     data = {
#         "area_id": area.value,
#         "order": order.value,
#         "is_finish": status.value,
#         "is_free": payment.value,
#         "style_id": style.value,
#         "page_num": pn,
#         "page_size": ps,
#     }
#     return (
#         await Api(**api, credential=credential, no_csrf=True)
#         .update_data(**data)
#         .update_params(**params)
#         .result
#     )


# async def get_manga_index(
#     area: MangaIndexFilter.Area = MangaIndexFilter.Area.ALL,
#     order: MangaIndexFilter.Order = MangaIndexFilter.Order.HOT,
#     status: MangaIndexFilter.Status = MangaIndexFilter.Status.ALL,
#     payment: MangaIndexFilter.Payment = MangaIndexFilter.Payment.ALL,
#     style: MangaIndexFilter.Style = MangaIndexFilter.Style.ALL,
#     pn: int = 1,
#     ps: int = 18,
#     credential: Credential = None,
# ) -> List[Manga]:
#     """
#     # 失效 2025-01-27
#     获取漫画索引

#     Args:

#         area    (MangaIndexFilter.Area)   : 地区。Defaults to MangaIndexFilter.Area.ALL.

#         order   (MangaIndexFilter.Order)  : 排序。Defaults to MangaIndexFilter.Order.HOT.

#         status  (MangaIndexFilter.Status) : 状态。Defaults to MangaIndexFilter.Status.ALL.

#         payment (MangaIndexFilter.Payment): 支付。Defaults to MangaIndexFilter.Payment.ALL.

#         style   (MangaIndexFilter.Style)  : 风格。Defaults to MangaIndexFilter.Style.ALL.

#         pn      (int)                     : 页码。Defaults to 1.

#         ps      (int)                     : 每页数量。Defaults to 18.

#         credential (Credential)           : 凭据类. Defaults to None.

#     Returns:
#         List[Manga]: 漫画索引
#     """
#     data = await get_raw_manga_index(
#         area, order, status, payment, style, pn, ps, credential
#     )
#     return [Manga(manga_data["season_id"]) for manga_data in data]


async def get_manga_update(
    date: Union[str, datetime.datetime] = datetime.datetime.now(),
    pn: int = 1,
    ps: int = 8,
    credential: Credential = None,
) -> List[Manga]:
    """
    获取更新推荐的漫画

    Args:
        date (Union[str, datetime.datetime]): 日期，默认为今日。

        pn   (int)                          : 页码。Defaults to 1.

        ps   (int)                          : 每页数量。Defaults to 8.

        credential (Credential)           : 凭据类. Defaults to None.

    Returns:
        List[Manga]: 漫画列表
    """
    credential = credential if credential else Credential()
    api = API["info"]["update"]
    params = {"device": "pc", "platform": "web", "nov": 25}
    if isinstance(date, datetime.datetime):
        date = date.strftime("%Y-%m-%d")
    data = {"date": date, "page_num": pn, "page_size": ps}
    manga_data = (
        await Api(**api, credential=credential, no_csrf=True)
        .update_data(**data)
        .update_params(**params)
        .result
    )
    return [Manga(manga["comic_id"]) for manga in manga_data["list"]]


async def get_manga_home_recommend(
    pn: int = 1, seed: Optional[str] = "0", credential: Credential = None
) -> List[Manga]:
    """
    获取首页推荐的漫画

    Args:
        pn   (int)                          : 页码。Defaults to 1.

        seed (Optional, str)                : Unknown param，无需传入.

        credential (Credential)           : 凭据类. Defaults to None.

    Returns:
        List[Manga]: 漫画列表
    """
    credential = credential if credential else Credential()
    api = API["info"]["home_recommend"]
    params = {"device": "pc", "platform": "web", "nov": 25}
    data = {"page_num": pn, "seed": seed}
    manga_data = (
        await Api(**api, credential=credential, no_csrf=True)
        .update_data(**data)
        .update_params(**params)
        .result
    )
    return [Manga(manga["comic_id"]) for manga in manga_data["list"]]
